#include "Commissioning.h"
#include "flash-board.h"
#include "system.h"
#include "tremo_uart.h"

SystemParmData_t SystemParmData = {0};
SystemLoRaData_t SystemLoRaData = {0};
// SystemAttribute_t SystemParmData = {0};

log_level_t g_log_level = LL_ALL;

const uint8_t DefaultDevEui[] = LORAWAN_DEVICE_EUI;
const uint8_t DefaultAppEui[] = LORAWAN_APPLICATION_EUI;
const uint8_t DefaultAppKey[] = LORAWAN_APPLICATION_KEY;

#if (USE_LORAWAN_ABP == 1)
const uint8_t DefaultNwkSKey[] = LORAWAN_NWKSKEY;
const uint8_t DefaultAppSKey[] = LORAWAN_APPSKEY;
#endif

uint8_t CRC8(unsigned char* ptr, unsigned char len)
{
    uint8_t crc;
    uint8_t i;
    crc = 0;
    while (len--) {
        crc ^= *ptr++;
        for (i = 0; i < 8; i++) {
            if (crc & 0x01) {
                crc = (crc >> 1) ^ 0x8C;
            } else
                crc >>= 1;
        }
    }
    return crc;
}

void SystemParmDataStorage(uint32_t addr)
{
    SystemParmData.crc = 0;
    SystemParmData.crc = CRC8((void*)&(SystemParmData), sizeof(SystemParmData) - 1);
    for (uint8_t i = 0; i < 3; i++) {
        if (EepromWriteData(addr, (void*)&(SystemParmData), sizeof(SystemParmData)) == 0) {
            break;
        }
    }
}

void SystemParmDataLoad(void)
{
    uint8_t crc = 0;
    uint8_t i = 0;
    memset1((void*)(&(SystemParmData)), 0, sizeof(SystemParmData));
    EepromReadData(SYSTEM_PARAM_DATA_ADDR, (void*)&(SystemParmData), sizeof(SystemParmData));
    crc = CRC8((void*)&(SystemParmData), sizeof(SystemParmData) - 1);
    LOG_PRINTF(LL_DEBUG, "SystemParmDataLoad:SystemParmData.crc=0x%0x,crc=0x%0x\r\n", SystemParmData.crc, crc);

    if ((crc != SystemParmData.crc) /* || ((crc == 0) || (SystemParmData.crc == 0))*/) {
        memset1((void*)&(SystemParmData), 0, sizeof(SystemParmData));
        crc = 0;
        EepromReadData(SYSTEM_PARAM_DATA_BACKUP_ADDR, (void*)&(SystemParmData), sizeof(SystemParmData));
        crc = CRC8((void*)&(SystemParmData), sizeof(SystemParmData) - 1);

        if ((crc == SystemParmData.crc) /*&& ((crc != 0) && (SystemParmData.crc != 0))*/) {
            LOG_PRINTF(LL_DEBUG, "load parm data error,reset backup data\r\n");
            SystemParmDataStorage(SYSTEM_PARAM_DATA_ADDR);
        } else {
            LOG_PRINTF(LL_DEBUG, "load parm data error,backup error,reset dufault data\r\n");
            memset1((void*)&(SystemParmData), 0, sizeof(SystemParmData));

            SystemParmData.activitionType = OVER_THE_AIR_ACTIVATION;
            SystemParmData.txConfirmedType = LORAWAN_CONFIRMED_MSG_ON;
            SystemParmData.appDataRate = LORAWAN_DEFAULT_DATARATE;
            SystemParmData.appDataNbTrials = LORAWAN_DATANBTRIALS;
            SystemParmData.adrType = LORAWAN_ADR_ON;
            SystemParmData.power = TX_POWER_0;
            SystemParmData.appPort = LORAWAN_APP_PORT;
            SystemParmData.deviceClass = CLASS_C;
            SystemParmData.baudRate = 19200;

            memcpy1((void*)SystemParmData.devEui, DefaultDevEui, DEVICEEUI_LEN);
            memcpy1((void*)SystemParmData.appEui, DefaultAppEui, APPEUI_LEN);
            memcpy1((void*)SystemParmData.appKey, DefaultAppKey, APPKEY_LEN);

#if (USE_LORAWAN_ABP == 1)
            memcpy1((void*)SystemParmData.nwkSKey, DefaultNwkSKey, NWKSKEY_LEN);
            memcpy1((void*)SystemParmData.appSKey, DefaultAppSKey, APPSKEY_LEN);
            SystemParmData.devAddr = LORAWAN_DEVICE_ADDRESS;
            SystemParmData.netID = LORAWAN_NETWORK_ID;
#endif

            SystemParmData.rx2Channel.Datarate = LORAWAN_DEFAULT_RX2_DATARATE;
            SystemParmData.rx2Channel.Frequency = LORAWAN_DEFAULT_RX2_FREQUENCY;

            SystemParmData.channelsMask[0] = 0x0000;
            SystemParmData.channelsMask[1] = 0xFF00;
            SystemParmData.channelsMask[2] = 0x0000;
            SystemParmData.channelsMask[3] = 0x0000;
            SystemParmData.channelsMask[4] = 0x0000;
            SystemParmData.channelsMask[5] = 0x0000;

            SystemParmData.perferChannel = 0;
            SystemParmData.perferChannelIndex = 80;
            SystemParmData.perferDataRate = DR_2;

            SystemParmData.txDutyCycleTime = 30;
            SystemParmDataStorage(SYSTEM_PARAM_DATA_ADDR);
            SystemParmDataStorage(SYSTEM_PARAM_DATA_BACKUP_ADDR);
        }
    }

    // if (!LoadSystemData(SYSTEM_PARAM_DATA_ADDR + SYSTEM_ATTR_OFFSET, (uint8_t *)&(SystemParmData),
    // sizeof(SystemParmData))) {
    //     memset1((uint8_t *)&(SystemParmData), 0, sizeof(SystemParmData));

    //     SaveSystemData(SYSTEM_PARAM_DATA_ADDR + SYSTEM_ATTR_OFFSET, (uint8_t *)&SystemParmData,
    //     sizeof(SystemParmData));
    //     // SaveSystemData(SYSTEM_PARAM_DATA_BACKUP_ADDR + SYSTEM_ATTR_OFFSET, (uint8_t *)&SystemParmData,
    //     sizeof(SystemParmData));
    // }

    DumpSystemInfo();
}

void DumpSystemInfo()
{
    SystemParmData_t paramData;
    // SystemAttribute_t attrConfig;
    // LoadSystemData(SYSTEM_PARAM_DATA_ADDR, (uint8_t *)(&(paramData)), sizeof(paramData));
    // LoadSystemData(SYSTEM_PARAM_DATA_ADDR + SYSTEM_ATTR_OFFSET, (uint8_t *)&(attrConfig), sizeof(attrConfig));

    LOG_PRINTF(
        LL_DEBUG,
        "\r\n\r\n/"
        "******************************************************************************************************\r\n");
    LOG_PRINTF(LL_DEBUG, "softwareVersion = %s\r\n", SoftwareVersion);
    LOG_PRINTF(LL_DEBUG, "DevEui:");
    for (int i = 0; i < DEVICEEUI_LEN; i++) {
        LOG_PRINTF(LL_DEBUG, "%02X ", SystemParmData.devEui[i]);
    }
    LOG_PRINTF(LL_DEBUG, "\r\n");

    LOG_PRINTF(LL_DEBUG, "AppEui:");
    for (int i = 0; i < APPEUI_LEN; i++) {
        LOG_PRINTF(LL_DEBUG, "%02X ", SystemParmData.appEui[i]);
    }
    LOG_PRINTF(LL_DEBUG, "\r\n");

    LOG_PRINTF(LL_DEBUG, "AppKey:");
    for (int i = 0; i < APPKEY_LEN; i++) {
        LOG_PRINTF(LL_DEBUG, "%02X ", SystemParmData.appKey[i]);
    }
    LOG_PRINTF(LL_DEBUG, "\r\n");

    LOG_PRINTF(LL_DEBUG, "channel:");
    for (int i = 0; i < 6; i++) {
        LOG_PRINTF(LL_DEBUG, "0x%04x ", SystemParmData.channelsMask[i]);
    }
    LOG_PRINTF(LL_DEBUG, "\r\n");

    DumpComParams();
#if defined(CONFIG_DTU_485)
    DumpAttrData();

    DumpDeviceModel();
    DumpDevices();
#endif
    LOG_PRINTF(
        LL_DEBUG,
        "/******************************************************************************************************\r\n");
}

void DumpAttrData()
{
    LOG_PRINTF(LL_DEBUG, "\r\n\r\n\r\n-----------Device Attributes----------\r\n");
    for (int i = 0; i < SYSTEM_ATTRIBUTE_NUM; i++) {
        if (SystemParmData.attributeData[i].attrId != 0) {
            char cmd[256];
            int index = 0;
            for (int j = 0; j <= SystemParmData.attributeData[i].attrCmdLen; j++) {
                if (j != 0) {
                    sprintf(&cmd[index++], " ");
                }
                bool space = false;
                if (j == SystemParmData.attributeData[i].attrAddrOffset) {
                    sprintf(&cmd[index], "ADDR", strlen("ADDR"));
                    index += strlen("ADDR");
                    space = true;
                } else if (j == SystemParmData.attributeData[i].attrVerifyOffset) {
                    if (SystemParmData.attributeData[i].attrVerify == 1) {
                        sprintf(&cmd[index], "CHK", strlen("CHK"));
                        index += strlen("CHK");
                    } else {
                        sprintf(&cmd[index], "CRC", strlen("CRC"));
                        index += strlen("CRC");
                    }
                    space = true;
                } else if (j == SystemParmData.attributeData[i].attrRegOffset) {
                    sprintf(&cmd[index], "REG", strlen("REG"));
                    index += strlen("REG");
                    space = true;
                } else {
                    if (j < SystemParmData.attributeData[i].attrCmdLen) {
                        if (space) {
                            sprintf(&cmd[index++], " ");
                        }
                        sprintf(&cmd[index], "%02X", SystemParmData.attributeData[i].attrCmd[j]);
                        index += 2;
                    }
                }
            }
            LOG_PRINTF(LL_DEBUG, "%d: attrId = %d, attrCmd = [%s]\r\n", i, SystemParmData.attributeData[i].attrId, cmd);
        }
    }
}

void DumpDeviceModel()
{
    LOG_PRINTF(LL_DEBUG, "\r\n\r\n\r\n-----------Device Model----------\r\n");
    for (int i = 0; i < DEVICE_MODEL_NUM; i++) {
        if (SystemParmData.deviceModel[i].modelId == 0) {
            continue;
        }

        int modelId = SystemParmData.deviceModel[i].modelId;
        int attrId = SystemParmData.deviceModel[i].attrId;
        LOG_PRINTF(LL_DEBUG, "%d: modelId = %d, attrId = %d, models[<8]:---------------------\r\n", i, modelId, attrId);
        if (FindDeviceAttr(attrId) == NULL) {
            LOG_PRINTF(LL_DEBUG, "attrId = %d's protocol not found!---------------------\r\n", attrId);
        }
        LoraDeviceModel_t devModel = SystemParmData.deviceModel[i];
        for (int j = 0; j < LORAMODEL_MAX_NUM; j++) {
            if (devModel.model[j].acqType == 0) {
                continue;
            }
            char strReg[32] = {0};
            int index = 0;
            for (int k = 0; k < devModel.model[j].regLengh; k++) {
                sprintf(&strReg[index], "%02X", devModel.model[j].regAddr[k]);
                index += 2;
                if (k != devModel.model[j].regLengh - 1) {
                    strcat(strReg, " ");
                    index += 1;
                }
            }
            LOG_PRINTF(LL_DEBUG, "    %d: acqType = %d, regAddr = [%s], dataOffset = %d, dataLength = %d\r\n", j,
                       devModel.model[j].acqType, strReg, devModel.model[j].dataOffset, devModel.model[j].dataLen);
        }
    }
}

void DumpDevices()
{
    LOG_PRINTF(LL_DEBUG, "\r\n\r\n\r\n-----------Devices----------\r\n");
    for (int i = 0; i < SYSTEM_DEVICE_NUM; i++) {
        if (SystemParmData.loraDevice[i].modelId == 0) {
            continue;
        }

        int modelId = SystemParmData.loraDevice[i].modelId;
        char addr[64];
        int index = 0;
        for (int j = 0; j < SystemParmData.loraDevice[i].addrLength; j++) {
            sprintf(&addr[index], "%02X", SystemParmData.loraDevice[i].devAddr[j]);
            index += 2;
            if (j != SystemParmData.loraDevice[i].addrLength - 1) {
                strcat(addr, " ");
                index += 1;
            }
        }
        LOG_PRINTF(LL_DEBUG, "%d: modelId = %d, devAddr = %s\r\n", i, modelId, addr);
    }
}

void DumpComParams()
{
    char pariMode = 'N';
    switch (SystemParmData.parityMode) {
        case UART_PARITY_NO:
            pariMode = 'N';
            break;
        case UART_PARITY_EVEN:
            pariMode = 'E';
            break;
        case UART_PARITY_ODD:
            pariMode = 'O';
            break;
    }
    LOG_PRINTF(LL_DEBUG, "COM:%d, %c, %d, %d\r\n", SystemParmData.baudRate, pariMode, SystemParmData.dataWidth,
               SystemParmData.stopBit);
}

static int inHandlerMode(void)
{
    return __get_IPSR() != 0;
}

void OsMutexRelease(SemaphoreHandle_t mutex_id)
{
    portBASE_TYPE taskWoken = pdFALSE;

    if (inHandlerMode()) {
        xSemaphoreGiveFromISR(mutex_id, &taskWoken);
        portEND_SWITCHING_ISR(taskWoken);
    } else {
        xSemaphoreGive(mutex_id);
    }
}

SystemAttributeData_t* FindDeviceAttr(uint8_t attrId)
{
    for (int i = 0; i < SYSTEM_ATTRIBUTE_NUM; i++) {
        if (attrId != 0 && SystemParmData.attributeData[i].attrId == attrId) {
            return &SystemParmData.attributeData[i];
        }
    }
    return NULL;
}

SystemAttributeData_t* GetEmptyDeviceAttr()
{
    for (int i = 0; i < SYSTEM_ATTRIBUTE_NUM; i++) {
        if (SystemParmData.attributeData[i].attrId == 0) {
            return &SystemParmData.attributeData[i];
        }
    }
    return NULL;
}

LoraDeviceModel_t* FindDeviceModel(int modelId)
{
    for (int i = 0; i < DEVICE_MODEL_NUM; i++) {
        if (modelId != 0 && SystemParmData.deviceModel[i].modelId == modelId) {
            return &SystemParmData.deviceModel[i];
        }
    }
    return NULL;
}

LoraDeviceModel_t* GetEmptyDeviceModel()
{
    for (int i = 0; i < DEVICE_MODEL_NUM; i++) {
        if (SystemParmData.deviceModel[i].modelId == 0) {
            return &SystemParmData.deviceModel[i];
        }
    }
    return NULL;
}
